﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Chefbook.HelperMethods
{
    /// <summary>
    /// Contains extension methods dealing with ICollections and IEnumerables
    /// </summary>
    public static partial class CollectionExtensionMethods
    {
        /// <summary>
        /// Adds an item to a collection if the collection does not already contain that item and the item is not null
        /// </summary>
        /// <typeparam name="T">The Type of the item to add</typeparam>
        /// <param name="collection">The collection to add to</param>
        /// <param name="itemToAdd">The item to add</param>
        /// <returns>True if item was added, else false</returns>
        public static bool AddIfNewAndNotNull<T>(this ICollection<T> collection, T itemToAdd)
        {
            if (!collection.Contains(itemToAdd) && itemToAdd != null)
            {
                collection.Add(itemToAdd);
                return true;
            }
            return false;
        }

        /// <summary>
        /// Adds an item to a collection unless the item is null or already exists in the collection
        /// </summary>
        /// <typeparam name="T">The type of the item to add</typeparam>
        /// <param name="collection">The collection to add to</param>
        /// <param name="itemToAdd">The item to add</param>
        public static void AddAndThrowIfNullOrNotNew<T>(this ICollection<T> collection, T itemToAdd)
        {
            if (itemToAdd == null)
            {
                throw new ArgumentNullException("itemToAdd");
            }
            if (collection.Contains(itemToAdd))
            {
                throw new ArgumentException("Item already exists in collection");
            }
            collection.Add(itemToAdd);
        }

        /// <summary>
        /// Overload of the "Contains" extension method that takes a Func in lieu of an IEqualityComparer
        /// </summary>
        /// <typeparam name="TSource">The type of the objects being compared</typeparam>
        /// <param name="list">The IEnumerable to check if for a contained object</param>
        /// <param name="value">The object to look for</param>
        /// <param name="comparer">A Func that takes 2 values and returns whether they are equal</param>
        /// <returns>True if value is found, else false</returns>
        public static bool Contains<TSource>(this IEnumerable<TSource> list, TSource value, Func<TSource, TSource, bool> comparer)
        {
            return list.Contains(value, new LambdaComparer<TSource>(comparer));
        }

        /// <summary>
        /// Adds and removes elements from the collection to modify so that it's contents match the new collection
        /// <remarks>The reason for this method instead of just reassigning the collection is specifically for ObservableCollection so only relevant CollectionChanged events are fired</remarks>
        /// </summary>
        /// <typeparam name="T">The type of the collection</typeparam>
        /// <param name="collectionToModify">The collection to modify</param>
        /// <param name="newCollection">The new collection that is modeled</param>
        public static void ModifyCollectionToMatchNewCollection<T>(this ICollection<T> collectionToModify, ICollection<T> newCollection)
        {
            List<T> itemsToRemove = new List<T>();
            foreach (var item in collectionToModify.Except(newCollection))
            {
                itemsToRemove.Add(item);
            }
            foreach (var item in itemsToRemove)
            {
                collectionToModify.Remove(item);
            }
            foreach (var item in newCollection.Except(collectionToModify))
            {
                collectionToModify.Add(item);
            }
        }

        /// <summary>
        /// Returns whether collection1 has the same entries as collection2
        /// </summary>
        /// <typeparam name="T">The type of the collection</typeparam>
        /// <param name="collection1">The first collection</param>
        /// <param name="collection2">The second collection</param>
        /// <returns>True if they contain the same values, else false</returns>
        public static bool HasSameEntriesAs<T>(this ICollection<T> collection1, ICollection<T> collection2)
        {
            if (collection1.Count != collection2.Count)
            { return false; }
            return collection1.Except(collection2).Count() == 0;
        }
    }
}
